/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * NodesPaintFrame.java
 *
 * Created on 9 Ιουλ 2009, 12:08:14 μμ
 */

package gui;

import utilities.DrawingBean;
import simulation.GUIOptionManager;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Random;
import java.util.Vector;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingConstants;

/**
 * The frame used to define the terrain details of nodes' positions
 * @author Arvanitis Ioannis
 */
public class NodesPaintFrame extends javax.swing.JFrame implements Runnable{
    private int terrain_width;
    private int terrain_height;
    private int obst_width;
    private int obst_height;
    private int numberOfCategories;
    private int nodesInCategoryTotal;;
    private int nodesInCategoryLeft;
    private int currentCategory;
    private Point p1, p2;
    private Vector<DrawingBean> v;
    private Thread runner;


    /**
     * Creates new form NodesPaintFrame
     * @param w Width (in meters) of canvas
     * @param h Height (in meters) of canvas
     * @param categories Total number of categories involved
     */
    public NodesPaintFrame(int w, int h, int categories) {
        this.terrain_width = w;
        this.terrain_height = h;
        this.numberOfCategories = categories;
        runner = new Thread(this);
        runner.start();
    }

    /**
     * Creates new form NodesPaintFrame
     * @param w Width (in meters) of canvas
     * @param h Height (in meters) of canvas
     * @param categories Total number of categories involved
     * @param w_obst Width (in cells) of canvas for obstacles
     * @param h_odst Height (in cells) of canvas for obstacles
     * @param v Vector containing the obstacles
     */
    public NodesPaintFrame(int w, int h, int categories, int w_obst, int h_obst, Vector<DrawingBean> v) {
        this.terrain_width = w;
        this.terrain_height = h;
        this.obst_width = w_obst;
        this.obst_height = h_obst;
        this.numberOfCategories = categories;
        this.v = v;
        runner = new Thread(this);
        runner.start();
    }

    public void run() {
        setTitle("Node positioning");
        Toolkit toolkit = Toolkit.getDefaultToolkit();
        Dimension screensize = toolkit.getScreenSize();
        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        //----------------------------------------------------------------------
        final NodesCanvasPanel canvas;
        if (v == null) {
            canvas = new NodesCanvasPanel(terrain_width,terrain_height);
        }
        else {
            canvas = new NodesCanvasPanel(terrain_width,terrain_height,obst_width,obst_height,v);
        }
        JPanel actions = new JPanel();

        final JLabel categories = new JLabel("1/"+numberOfCategories+" - 0/"+GUIOptionManager.getNodeArchPerCategory().get(0).getNumberOfNodes());
        categories.setHorizontalAlignment(SwingConstants.CENTER);
        nodesInCategoryTotal = Integer.parseInt(categories.getText().substring(categories.getText().lastIndexOf("/")+1));
        nodesInCategoryLeft = nodesInCategoryTotal - Integer.parseInt(categories.getText().substring(categories.getText().lastIndexOf("-")+2, categories.getText().lastIndexOf("/")));
        currentCategory = Integer.parseInt(categories.getText().substring(0, categories.getText().indexOf("/")));
        final JRadioButton random = new JRadioButton("Random", true);
        final JRadioButton custom = new JRadioButton("Custom", false);
        ButtonGroup color = new ButtonGroup();
        color.add(random);
        color.add(custom);
        NodesCanvasPanel.setColor(Color.RED);
        final JButton set = new JButton("Set positions");
        final JButton ok = new JButton("Ok");
        ok.setEnabled(false);
        JButton clearAll = new JButton("Clear all");
        final JLabel current = new JLabel();
        current.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);

        
        random.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                set.setEnabled(true);
            }
        });

        custom.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                set.setEnabled(false);
            }
        });

        set.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                
                for (int i=0; i<nodesInCategoryLeft; i++) {
                    Point pos = findFreePosition();
                    Vector<DrawingBean> tempv = NodesCanvasPanel.getNodes_v();
                    tempv.addElement(new DrawingBean(pos, NodesCanvasPanel.getColor(), ""+currentCategory));
                    NodesCanvasPanel.setNodes_v(tempv);
                }
                if (currentCategory < numberOfCategories) {
                    categories.setText((currentCategory+1)+"/"+numberOfCategories+" - 0/"+GUIOptionManager.getNodeArchPerCategory().get(currentCategory).getNumberOfNodes());
                    nodesInCategoryLeft = nodesInCategoryTotal = Integer.parseInt(categories.getText().substring(categories.getText().lastIndexOf("/")+1));
                    currentCategory++;
                    NodesCanvasPanel.setColor(new Color(255-10*(currentCategory-1), 15*(currentCategory-1), 25*(currentCategory-1)));
                }
                else {
                    nodesInCategoryLeft = 0;
                    categories.setText(numberOfCategories+"/"+numberOfCategories+" - "+nodesInCategoryTotal+"/"+nodesInCategoryTotal);
                    ok.setEnabled(true);
                }
                repaint();
            }
        });

        ok.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                GUIOptionManager.setNodes(NodesCanvasPanel.getNodes_v());
                dispose();
            }
        });

        clearAll.addActionListener(new ActionListener() {

            public void actionPerformed(ActionEvent e) {
                NodesCanvasPanel.getNodes_v().removeAllElements();
                categories.setText("1/"+numberOfCategories+" - 0/"+GUIOptionManager.getNodeArchPerCategory().get(0).getNumberOfNodes());
                nodesInCategoryTotal = Integer.parseInt(categories.getText().substring(categories.getText().lastIndexOf("/")+1));
                nodesInCategoryLeft = nodesInCategoryTotal - Integer.parseInt(categories.getText().substring(categories.getText().lastIndexOf("-")+2, categories.getText().lastIndexOf("/")));
                currentCategory = Integer.parseInt(categories.getText().substring(0, categories.getText().indexOf("/")));
                ok.setEnabled(false);
                NodesCanvasPanel.setColor(Color.RED);
                repaint();
            }
        });

        canvas.addMouseListener(new MouseListener() {

            /**
             * Mouse was clicked in the canvas area
             * @param e MouseEvent
             */
            public void mouseClicked(MouseEvent e) {
                Point position = canvas.findCurrentPosition(new Point(e.getPoint()));
                if (custom.isSelected() && inCanvas(e.getPoint()) && isFreePosition(position)) {
                    if (nodesInCategoryLeft > 0) {
                        nodesInCategoryLeft--;
                        Vector<DrawingBean> tempv = NodesCanvasPanel.getNodes_v();
                        tempv.addElement(new DrawingBean(position, NodesCanvasPanel.getColor(), ""+currentCategory));
                        NodesCanvasPanel.setNodes_v(tempv);
                        categories.setText(currentCategory+"/"+numberOfCategories+" - "+(nodesInCategoryTotal-nodesInCategoryLeft)+"/"+nodesInCategoryTotal);
                    }
                    if (nodesInCategoryLeft == 0) {
                        if (currentCategory < numberOfCategories) {
                            categories.setText((currentCategory+1)+"/"+numberOfCategories+" - 0/"+GUIOptionManager.getNodeArchPerCategory().get(currentCategory).getNumberOfNodes());
                            nodesInCategoryLeft = nodesInCategoryTotal = Integer.parseInt(categories.getText().substring(categories.getText().lastIndexOf("/")+1));
                            currentCategory++;
                            NodesCanvasPanel.setColor(new Color(255-10*(currentCategory-1), 15*(currentCategory-1), 25*(currentCategory-1)));
                        }
                        else {
                            categories.setText(numberOfCategories+"/"+numberOfCategories+" - "+nodesInCategoryTotal+"/"+nodesInCategoryTotal);
                            ok.setEnabled(true);
                        }
                    }
                }
                repaint();
            }

            /**
             * Mouse was pressed in the canvas area
             * @param e MouseEvent
             */
            public void mousePressed(MouseEvent e) {
                p1 = e.getPoint();
            }

            /**
             * Mouse was released in the canvas area
             * @param e MouseEvent
             */
            public void mouseReleased(MouseEvent e) {
                p2 = e.getPoint();
                if (canvas.findCurrentPosition(p1).x == canvas.findCurrentPosition(p2).x && canvas.findCurrentPosition(p1).y == canvas.findCurrentPosition(p2).y) {
                    mouseClicked(e);
                }
            }

             /**
             * Mouse entered the canvas area
             * @param e MouseEvent
             */
            public void mouseEntered(MouseEvent e) {
                setCursor(new Cursor(Cursor.HAND_CURSOR));
            }

            /**
             * Mouse exited the canvas area
             * @param e MouseEvent
             */
            public void mouseExited(MouseEvent e) {
                setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
            }
        });

        canvas.addMouseMotionListener(new MouseMotionListener() {

            /**
             * Mouse was dragged in the canvas area
             * @param e MouseEvent
             */
            public void mouseDragged(MouseEvent e) {
                mouseMoved(e);
            }

            /**
             * Mouse was moved in the canvas area
             * @param e MouseEvent
             */
            public void mouseMoved(MouseEvent e) {
                Point p = e.getPoint();
                if (inCanvas(p)) {
                    p = canvas.findCurrentPosition(p);
                    current.setText(p.x+","+p.y);
                }
                else {
                    current.setText(null);
                }
                repaint();
            }
        });

        
        actions.setLayout(new GridLayout(1, 7));
        actions.add(categories);
        actions.add(random);
        actions.add(custom);
        actions.add(set);
        actions.add(ok);
        actions.add(clearAll);
        actions.add(current);

        getContentPane().add(canvas, BorderLayout.CENTER);
        getContentPane().add(actions, BorderLayout.SOUTH);

        //----------------------------------------------------------------------

        setSize(canvas.getSize());
        int x = (int) ( screensize.getWidth() - getWidth() ) / 2;
        int y = (int) ( screensize.getHeight() - getHeight() ) / 2;
        setLocation( x, y );
        setVisible(true);
    }



    /**
     * Finds randomly a free position for our node
     * @return randomly a free position for our node
     */
    private Point findFreePosition() {
        Random generator = new Random();
        while (true) {
            int x = (int) Math.round(terrain_width * generator.nextDouble());
            int y = (int) Math.round(terrain_height * generator.nextDouble());
            if (isFreePosition(new Point(x,y))) {
                return new Point(x,y);
            }
        }
    }

    /**
     * Determines whether a given position is free
     * @param p Point that refers to the specified position to be checked
     * @return Whether or not a specific position is occupied
     */
    private boolean isFreePosition(Point p) {
        if (isOccupied(p, NodesCanvasPanel.getObstacles_v(), NodesCanvasPanel.getNodes_v())) {
            return false;
        }
        else {
            return true;
        }
    }

    /**
     * Examines whether the specified position is occupied/blocked or free to place a node
     * @param p Point under examination
     * @param obstacles_v Vector with obstacles (in grid)
     * @param nodes_v Vector with nodes' position details
     * @return whether the specified position is occupied/blocked or free to place a node
     */
    private boolean isOccupied (Point p, Vector<DrawingBean> obstacles_v, Vector<DrawingBean> nodes_v) {
        if (obstacles_v != null) {
            for (int i=0; i<obstacles_v.size(); i++) {
                if (obstacles_v.get(i).getPoint().x * terrain_width/obst_width <= p.x && (obstacles_v.get(i).getPoint().x+1) * terrain_width/obst_width >= p.x && obstacles_v.get(i).getPoint().y * terrain_height/obst_height <= p.y && (obstacles_v.get(i).getPoint().y+1) * terrain_height/obst_height >= p.y) {
                    return true;
                }
            }
        }
        if (nodes_v != null) {
            for (int i=0; i<nodes_v.size(); i++) {
                if (nodes_v.get(i).getPoint().x == p.x && nodes_v.get(i).getPoint().y == p.y) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Determines whether a given pixel is contained or not in a specified area
     * @param p Given point indicating the distance from the upper left corner
     * @return whether or not this point is located in the defined canvas
     */
    private boolean inCanvas(Point p) {
        if (NodesCanvasPanel.getObstacles_v().size() > 0) {
            if (p.x < obst_width*NodesCanvasPanel.getCell_width() && p.y < obst_height*NodesCanvasPanel.getCell_height()) {
                return true;
            }
            else {
                return false;
            }
        }
        else {
            return true;
        }
    }

    

    
    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 400, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 300, Short.MAX_VALUE)
        );

        pack();
    }// </editor-fold>//GEN-END:initComponents


    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables

}
